#ifdef WIN32
#include <windows.h>
#endif

#include "Enums.h"
#include "Splitter.h"
#include "Skinner.h"
#include "Skinner_Brute.h"
#include "Skinner_Cored.h"
#include "Skinner_Incremental.h"
#include "Builder.h"
#include "Builder_Recursive.h"
#include "Builder_Backtracking.h"
#include "Pile.h"
#include "Fixer.h"
#include <iostream>
#include <fstream>

#include "Parser.h"
#include "Log.h"
#include "PS_Log.h"

std::string data_filename;
std::string output_base_name;
double min_good_split_ratio;

void configure() {
    std::ifstream config("config.txt");
        if (!config.is_open()) {
        std::cerr << "Open config.txt failed\n";
                exit(1);
        }

    LOG_CLEAR_DETAILS;
    LOG_SHOW_TIME_STAMP(false);

    int n_errors = 0;
    while (!config.eof()) {
        std::string text_line;
        std::getline(config, text_line);
        Parser p(text_line);

        if (p.done()) {
            continue;   // skip blanks
        }

        int m=0;

        if ((m = p.match_word("log_file"))) {
            std::string filename;
            m += p.match_any_word(filename);
            Log::set_stream_file(filename);
//            std::ofstream log_stream(filename.c_str());
//            LOG_SET_STREAM(log_stream);
        }

        else if ((m = p.match_word("input"))) {
            m += p.match_any_word(data_filename);
        }

        else if ((m = p.match_word("output_base"))) {
            m += p.match_any_word(output_base_name);
        }

        else if ((m = p.match_word("min_split_ratio"))) {
            m += p.match_double(min_good_split_ratio);

            std::cerr << "config: ratio=" << min_good_split_ratio << "\n";
        }

        else if ((m = p.match_word("debug"))) {
            std::string detail_name;
            m += p.match_any_word(detail_name);
            LOG_ACTIVATE_DETAIL_NAME(detail_name);
        }

        if (m != 2) {
            std::cerr << "Bad config line: \'" << text_line << "\'\n";
            n_errors++;
        }
    }

    if (n_errors != 0) {
        std::cerr << "Config file is bad.\n";
        exit(1);
    }
}

int main(int argc, char** argv) {

    configure();

    LOG_ENTER(MAIN, "main");

    std::string postscript_log = output_base_name + "-log.ps";
    PS_Log::open(postscript_log);

    try {
        Object_List items;
        LOG(MAIN) << "allocated object list";

        items.read(data_filename);

        LOG(MAIN) << "done reading objects.  They are:";
        items.write(Log::output);

        Fixer fixer(min_good_split_ratio, &items,
                    Cluster_Distance::SINGLE,
//                    Cluster_Distance::AVERAGE,
                    Representative_Set::WHOLE);

        LOG(MAIN) << "allocated fixer";

//    Skinner *skinner = new Brute_Skinner();
        int extreme_set_size   = std::max(3,(int)(log(items.size())
                                                  /log(2)));
        Skinner *skinner = new Skinner_Cored(3, extreme_set_size);

        Splitter *splitter = new Splitter(skinner);

        LOG(MAIN) << "allocated splitter";

        Builder *builder = new Builder_Backtracking(&fixer, splitter);

        LOG(MAIN) << "allocated builder";

        fixer.set_tree(&builder->tree);


        Cluster root(items.get_whole_span(), 0);

        LOG(MAIN) << "allocated root node of CT";

        Labeled_Metric_Object::num_distance_calcs = 0;

        builder->build(root);

        LOG(MAIN) << "Did " << Labeled_Metric_Object::num_distance_calcs
                  << " distance calcs\n";

        builder->tree.preorder_write(Log::output);

        std::string labeled_items_file = output_base_name + "-labeled.txt";
        items.write(labeled_items_file);

        std::string tree_file = output_base_name + "-tree.txt";
        builder->tree.write(tree_file);

        std::string node_file = output_base_name + "-nodes.txt";
        builder->tree.write_nodes(node_file);
    }

    catch (std::string err_msg)
    {
        LOG(ERROR) << "FATAL: " << err_msg << "\n";
        std::cerr << "FATAL: " << err_msg << "\n";
    }

    PS_Log::close();

    LOG_EXIT();

    exit(0);
}
